package com.rdgzs.framework.dao;

import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.hibernate.Hibernate;
import org.hibernate.SQLQuery;
import org.hibernate.type.Type;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springside.modules.orm.Page;
import org.springside.modules.orm.hibernate.HibernateDao;

@Component
public class DylkHibernateDao extends HibernateDao<Object, String> {

	private static Logger logger = LoggerFactory.getLogger(DylkHibernateDao.class);

	public void setEntityClass(Class entityClass) {
		this.entityClass = entityClass;
	}

	public Object getEntity(Class entityClass, Serializable id) {
		Assert.notNull(entityClass, "entityClass\u4E0D\u80FD\u4E3A\u7A7A");
		Assert.notNull(id, "id\u4E0D\u80FD\u4E3A\u7A7A");
		return getSession().load(entityClass, id);
	}

	public List<Object> findEntityListBy(Class entityClass, String propertyName, Object value) {
		setEntityClass(entityClass);
		return findBy(propertyName, value);
	}

	public Page<Object> findPageBySql(Page page, String sql, List values, Class entityClass) {
		Assert.notNull(page, "page\u4E0D\u80FD\u4E3A\u7A7A");
		SQLQuery q = createSQLQuery(sql);
		if (page.isAutoCount()) {
			long totalCount = countSqlResult(sql, values);
			page.setTotalCount(totalCount);
		}

		prepareSQLQueryParams(q, values);
		setPageParameterToQuery(q, page);
		if (entityClass != null) {
			q.addEntity(entityClass);
		}
		List result = q.list();
		page.setResult(result);
		return page;
	}

	public Page<Object> findPageBySql(Page<Object> page, String sql, List<Object> values) {
		return findPageBySql(page, sql, values, null);
	}

	public long countSqlResult(String sql, List<Object> values) {
		String countSql = prepareCountSql(sql);
		try {
			BigInteger biCount = (BigInteger) findUniqueBySql(countSql, values);
			if(biCount!=null) {
				return biCount.longValue();
			}
			return 0;
		} catch (Exception e) {
			throw new RuntimeException((new StringBuilder()).append(
					"sql can't be auto count, sql is:").append(countSql).toString(), e);
		}
	}

	public Object findUniqueBySql(String sql, List<Object> values) {
		SQLQuery sqlQuery = createSQLQuery(sql);
		prepareSQLQueryParams(sqlQuery, values);
		return sqlQuery.uniqueResult();
	}

	public List findAllBySql(String sql, List<Object> values) {
		SQLQuery sqlQuery = createSQLQuery(sql);
		prepareSQLQueryParams(sqlQuery, values);
		return sqlQuery.list();
	}

	public int updateByFullSQL(String strSql, List<Object> sqlParams) {
		SQLQuery q = createSQLQuery(strSql);
		prepareSQLQueryParams(q, sqlParams);
		return q.executeUpdate();
	}

	private SQLQuery createSQLQuery(String sql) {
		return getSession().createSQLQuery(sql);
	}

	private String prepareCountSql(String orgSql) {
		String fromSql = orgSql;
		fromSql = (new StringBuilder()).append("from ").append(
				StringUtils.substringAfter(fromSql, "from")).toString();
		fromSql = StringUtils.substringBefore(fromSql, "order by");
		String countSql = (new StringBuilder()).append("select count(1) ").append(fromSql)
				.toString();
		return countSql;
	}

	private void prepareSQLQueryParams(SQLQuery sqlQuery, List<Object> params) {
		if (params == null || params.size() <= 0)
			return;
		Object value = null;
		for (int i = 0; i < params.size(); sqlQuery.setParameter(i++, value, judgeType(value)))
			value = params.get(i);

	}

	private Type judgeType(Object obj) {
		if (obj == null)
			throw new AssertionError();
		Type type = null;
		if (obj instanceof String)
			type = Hibernate.STRING;
		else if (obj instanceof Integer)
			type = Hibernate.INTEGER;
		else if (obj instanceof Long)
			type = Hibernate.LONG;
		else if (obj instanceof Short)
			type = Hibernate.SHORT;
		else if (obj instanceof Float)
			type = Hibernate.FLOAT;
		else if (obj instanceof Double)
			type = Hibernate.DOUBLE;
		else if (obj instanceof Date)
			type = Hibernate.DATE;
		else if (obj instanceof Time)
			type = Hibernate.TIME;
		else if (obj instanceof Timestamp)
			type = Hibernate.TIMESTAMP;
		else if (obj instanceof Byte)
			type = Hibernate.BYTE;
		else if (obj instanceof Boolean)
			type = Hibernate.BOOLEAN;
		else if (obj instanceof BigDecimal)
			type = Hibernate.BIG_DECIMAL;
		else if (obj instanceof Calendar)
			type = Hibernate.CALENDAR;
		else if (obj instanceof Character)
			type = Hibernate.CHARACTER;
		else
			type = Hibernate.CHARACTER;
		return type;
	}

}
